package com.na.game.engine.widget;

import android.graphics.Rect;
import android.view.MotionEvent;

import com.na.game.engine.GameManager;
import com.na.game.engine.event.Event;
import com.na.game.engine.event.EventListener;
import com.na.game.engine.event.EventManager;
import com.na.game.engine.event.Updater;
import com.na.game.engine.event.impl.KeyEvent;
import com.na.game.engine.event.impl.TouchEvent;
import com.na.game.engine.graphics.Graphics;
import com.na.game.engine.graphics.Painter;
import com.na.game.engine.util.Util;
import com.na.game.engine.widget.impl.Container;

public abstract class Widget implements Painter, EventListener, Updater {
	
	protected Rect bound;
	protected Rect absBound;
	protected Container parent;
	protected EventListener eventListener;
	protected Painter painter;
	protected Updater updater;
	protected boolean dirty;
	protected Rect src;
	protected boolean scaled;
	protected int status;
	public static final int STATUS_UP = 0;
	public static final int STATUS_DOWN = 1;
	
	protected Object extra;
	protected String name; // debug use
	protected boolean visible;
	protected float scale;
	protected int backgroundColor;
	
	public Widget() {
		bound = new Rect();
		absBound = new Rect();
		dirty = true;
		visible = true;
		scale = 1.0f;
	}
	
	public void setBound(int x, int y, int width, int height) {
		bound.set(x, y, x + width, y + height);
		if (parent != null) {
			absBound.set(parent.absBound.left + x, parent.absBound.top + y, parent.absBound.left + x + width, parent.absBound.top + y + height);
		} else {
			absBound.set(bound);
		}
	}
	
	public void setBound(Rect rect) {
		setBound(rect.left, rect.top, rect.width(), rect.height());
	}
	
	public Rect getBound() {
		return bound;
	}
	
	public Rect getAbsBound() {
		return absBound;
	}
	
	public int getAbsX() {
		return absBound.left;
	}
	
	public int getAbsY() {
		return absBound.top;
	}
	
	public int getX() {
		return bound.left;
	}
	
	public int getY() {
		return bound.top;
	}
	
	public int getWidth() {
		return bound.width();
	}
	
	public int getHeight() {
		return bound.height();
	}
	
	public void setParent(Container parent) {
		this.parent = parent;
	}
	
	public void setEventListener(EventListener listener) {
		eventListener = listener;
	}
	
	public void setPainter(Painter painter) {
		this.painter = painter;
	}
	
	public void setUpdater(Updater updater) {
		this.updater = updater;
	}
	
	public void setDirty() {
		dirty = true;
	}
	
	public void setSrcRect(Rect src) {
		this.src = src;
	}
	
	public void setScaled(boolean scaled) {
		this.scaled = scaled;
	}
	
	public void setExtra(Object extra) {
		this.extra = extra;
	}
	
	public Object getExtra() {
		return extra;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public int getStatus() {
		return status;
	}
	
	public void setVisible(boolean visible) {
		this.visible = visible;
	}
	
	public boolean isVisible() {
		return visible;
	}

	@Override
	public boolean onKey(KeyEvent event) {
		if (visible && eventListener != null) {
			return eventListener.onKey(event);
		}
		return false;
	}

	@Override
	public boolean onTouch(TouchEvent event) {
		if (!visible) {
			return false;
		}
		switch (event.action) { // default onTouch
			case MotionEvent.ACTION_DOWN:
				GameManager.get(EventManager.class).setFocusWidget(this);
				status = STATUS_DOWN;
				break;
			case MotionEvent.ACTION_MOVE:
				Widget focus = GameManager.get(EventManager.class).getFocusWidget();
				if (focus != null && focus != this && focus != parent) {
					// fire a outside event
					focus.onTouch(new TouchEvent(Event.TOUCH_EVENT, MotionEvent.ACTION_OUTSIDE, event.x, event.y));
				}
				break;
			case MotionEvent.ACTION_UP:
			case MotionEvent.ACTION_OUTSIDE:
				GameManager.get(EventManager.class).setFocusWidget(null);
				status = STATUS_UP;
				break;
		}
		if (eventListener != null) {
			boolean handled = eventListener.onTouch(event);
			return handled;
		}
		return false;
	}

	@Override
	public void paint(Graphics g) {
		if (visible) {
			float oldScale = g.getScale();
			if (scale != 1.0f) {
				g.setScale(scale * oldScale);
			}
			reset(g);
			if (backgroundColor != 0) {
				g.setColor(backgroundColor);
				g.fillRect(absBound.left, absBound.top, absBound.width(), absBound.height());
			}
			paintImpl(g);
			if (painter != null) {
				painter.paint(g);
			}
			g.setColor(0xFFFFFFFF);
			if (scale != 1.0f) {
				g.setScale(oldScale);
			}
		}
	}
	
	protected void reset(Graphics g) {
		Rect rect = intersect();
		g.setClip(rect.left, rect.top, rect.width(), rect.height());
	}
	
	protected Rect intersect() {
		if (dirty) {
			dirty = false;
			setBound(bound);
		}
		if (parent != null) {
			Rect rect = new Rect(absBound);
			Util.intersect(rect, parent.intersect());
			return rect;
		}
		return absBound;
	}

	@Override
	public void update() {
		if (visible && updater != null) {
			updater.update();
		}
	}

	@Override
	public String toString() {
		return super.toString() + "[" + name + "]";
	}
	
	public void setScale(float scale) {
		this.scale = scale;
	}
	
	public void setBackgroundColor(int color) {
		backgroundColor = color;
	}
	
	protected abstract void paintImpl(Graphics g);
}
